# 2023/7/23 12:42
# xiashuobad
# PyCharm
"""
 * _ooOoo_
 * o8888888o
 * 88" . "88
 * (| -_- |)
 *  O\ = /O
 * ___/`---'\____
 * .   ' \\| |// `.
 * / \\||| : |||// \
 * / _||||| -:- |||||- \
 * | | \\\ - /// | |
 * | \_| ''\---/'' | |
 * \ .-\__ `-` ___/-. /
 * ___`. .' /--.--\ `. . __
 * ."" '< `.___\_<|>_/___.' >'"".
 * | | : `- \`.;`\ _ /`;.`/ - ` : | |
 * \ \ `-. \_ __\ /__ _/ .-` / /
 * ======`-.____`-.___\_____/___.-`____.-'======
 * `=---=' bug泛滥 佛已瘫痪
"""
import os
import shutil
from itertools import chain
from pathlib import Path, WindowsPath, PosixPath
from tqdm import tqdm


def copy_img_files_by_filename(list_img_dirs, filename: str, dst_root_dir):
    """
    同步复制多个目录下图片文件名为filename的图片到dst_root_dir目录下对应的目录下(保持原来的最后一级目录结构)
    :param list_img_dirs: 多组图片的目录列表
    :param filename: 图片文件名,不包括后缀
    :param dst_root_dir: 拷贝输出根目录
    :return:
    """
    filename = MyPath(filename).stem
    list_img_suffix = get_list_suffix_from_dirs(list_img_dirs)
    assert len(list_img_dirs) == len(list_img_suffix)
    for img_dir, suffix in zip(list_img_dirs, list_img_suffix):
        img_path = MyPath(img_dir, f'{filename}{suffix}')
        dst_dir = MyPath(dst_root_dir) / MyPath(img_dir).stem
        MyPath(dst_dir).makedirs()
        try:
            shutil.copy(img_path, dst_dir)
        except Exception as e:
            print(f'错误：{e}')


def remove_img_files_by_filename(list_img_dirs, filename, list_img_suffix=None):
    """
    同步删除多个目录下图片文件名为filename的图片,filename必须是第一个目录下的
    :param list_img_dirs: 多组图片的目录列表
    :param filename: 图片文件名
    :param list_img_suffix:
    :return:
    """
    if not list_img_suffix:
        list_img_suffix = get_list_suffix_from_dirs(list_img_dirs)
    assert len(list_img_dirs) == len(list_img_suffix)
    for i, img_dir in enumerate(list_img_dirs):
        img_path = MyPath(img_dir, filename.replace(list_img_suffix[0], list_img_suffix[i]))
        img_path.unlink(missing_ok=True)


def remove_img_files_not_in_reference(list_img_dirs: (list, str), refer_img_dir: str, refer_img_suffix='.tif'):
    """
    批量删除多个目录下的在参考目录中不存在的文件
    :param list_img_dirs:
    :param refer_img_dir:
    :param refer_img_suffix:
    :return:
    """
    if isinstance(list_img_dirs, str):
        list_img_dirs = [list_img_dirs]
    list_img_suffix = get_list_suffix_from_dirs(list_img_dirs)  # 获取所有目录各自的后缀
    for img_name in tqdm(MyPath(list_img_dirs[0]).list_dir()):
        refer_img_name = img_name.replace(list_img_suffix[0], refer_img_suffix)
        refer_img_path = MyPath(refer_img_dir, refer_img_name)
        if not MyPath(refer_img_path).exists():
            remove_img_files_by_filename(list_img_dirs, img_name, list_img_suffix)


def move_img_files_by_filename(list_img_dirs, filename, dst_root_dir):
    """
        同步移动多个目录下图片文件名为filename的图片到dst_root_dir目录下对应的目录下(保持原来的最后一级目录结构)
        :param list_img_dirs: 多组图片的目录列表
        :param filename: 图片文件名
        :param dst_root_dir: 拷贝输出根目录
        :return:
        """
    filename = MyPath(filename).stem
    list_img_suffix = get_list_suffix_from_dirs(list_img_dirs)
    assert len(list_img_dirs) == len(list_img_suffix)
    for img_dir, suffix in zip(list_img_dirs, list_img_suffix):
        img_dir = MyPath(img_dir)
        img_path = img_dir / f'{filename}{suffix}'
        dst_dir = MyPath(dst_root_dir) / img_dir.stem
        dst_dir.makedirs()
        try:
            dst_path = img_path.replace_parent(dst_dir)
            dst_path.unlink(missing_ok=True)
            shutil.move(img_path.path, dst_dir.path)
        except Exception as e:
            print(f'错误：{e}')


def diff_two_str(path1: str, path2: str):
    """
    比较两个路径path1和path2，返回三个值，分别为：两者相同前缀prefix，path1的后缀，path2的后缀
    Args:
        path1: 文件1
        path2: 文件2

    Returns: 相同前缀,path1后缀，path2后缀

    """
    path1, path2 = MyPath(path1), MyPath(path2)
    if (prefix := path1.stem) == path2.stem:  # 如果两个路径的文件名一样
        return prefix, path1.suffix, path2.suffix
    i = 0
    path1, path2 = path1.name, path2.name
    while i < min(len(path1), len(path2)) and path1[i] == path2[i]:
        i += 1
    prefix = path1[:i]
    suffix1 = path1[i:]
    suffix2 = path2[i:]
    return prefix, suffix1, suffix2


def get_list_suffix_from_files(files: (str, tuple, list)):
    """
    自动获取多个文件的实际后缀
    Args:
        files: 一组文件路径

    Returns:

    """
    if isinstance(files, str):
        files = [files]
    assert isinstance(files, (list, tuple))
    if len(files) == 1:
        return [MyPath(files[0]).suffix]
    files = [MyPath(file).name for file in files]
    new_files = sorted(files, key=lambda file: len(file))
    indexes = [new_files.index(file) for file in files]
    prefix, suffix1, suffix2 = diff_two_str(new_files[0], new_files[1])
    list_suffix = [suffix1, suffix2]
    for new_file in new_files[2:]:
        suffix = new_file.replace(prefix, '', 1)
        list_suffix.append(suffix)
    return [list_suffix[index] for index in indexes]


def get_list_suffix_from_dirs(dirs: (list, tuple, str)):
    """
    自动获取各个目录下文件的不同后缀，只适用于同一目录下文件后缀相同的情况
    Args:
        dirs:

    Returns:
    """
    if isinstance(dirs, str):
        dirs = [dirs]
    assert isinstance(dirs, (list, tuple))
    list_files = [sorted(MyPath(_dir).list_dir())[0] for _dir in dirs]
    return get_list_suffix_from_files(list_files)


class MyPath(Path):
    def __new__(cls, *args, suffix: str = None, **kwargs):
        cls = MyWindowsPath if os.name == 'nt' else MyPosixPath
        self = super().__new__(cls, *args, **kwargs)
        self.real_suffix = self.suffix
        if suffix and str(args[-1]).endswith(suffix):
            self.real_suffix = suffix
        self.file_prefix = self.name.strip(self.real_suffix)
        return self

    def __init__(self, *args, suffix: str = None):
        pass

    def __len__(self):
        return len(self.path)

    def __truediv__(self, key) -> 'MyPath':
        return self.__class__(super().__truediv__(key))

    def joinpath(self, *args) -> 'MyPath':
        return self.__class__(super().joinpath(*args))

    def iter_dir(self, list_patterns: (list, tuple, str) = None, recursive=False):
        """
        根据模式匹配扫描目录，返回所有满足条件的文件路径生成器
        Args:
            list_patterns: 匹配模式，支持同时匹配多种文件类型，用列表或者元组
            recursive: 是否递归遍历所有子目录,仅匹配模式下有效
        Returns:
        """
        if not list_patterns:
            return map(self.__class__, self.iterdir())
        if isinstance(list_patterns, str):
            list_patterns = [list_patterns]
        assert isinstance(list_patterns, (list, tuple))
        res = chain()
        for pattern in list_patterns:
            res = chain(res, self.glob(pattern) if not recursive else self.rglob(pattern))
        return map(self.__class__, res)

    def list_dir(self, list_patterns: (list, tuple, str) = None, recursive=False):
        """
        根据模式匹配返回当前目录下满足条件的所有文件名列表，包括目录
        Args:
            list_patterns: 模式匹配
            recursive: 是否遍历所有子目录
        Returns:
        """
        list_file_paths = self.iter_dir(list_patterns, recursive)
        list_file_names = [filepath.name for filepath in list_file_paths]
        return list_file_names

    def make_parent_dirs(self, exist_ok=True):
        """
        自动创建所给文件路径的父目录
        Args:
            exist_ok:

        Returns:

        """
        self.parent.mkdir(parents=True, exist_ok=exist_ok)

    def replace_parent(self, new_parent: (str, Path)) -> 'MyPath':
        """
        替换当前路径的父目录的路径，并返回替换后的路径
        Args:
            new_parent:

        Returns:

        """
        return self.__class__(new_parent, self.name, suffix=self.real_suffix)

    def replace_suffix(self, new_suffix: str) -> 'MyPath':
        """
        替换当前路径的后缀名
        :param new_suffix:
        :return:
        """
        if self.real_suffix:  # 后缀不为空，表示为文件
            return self.__class__(self.parent, self.name.replace(self.real_suffix, new_suffix), suffix=new_suffix)
        else:  # 后缀为空，表示为目录
            return self.__class__(f'{self}{new_suffix}')

    def makedirs(self, exist_ok=True):
        """
        创建目录，并自动创建所有父目录，同os.makedirs
        Args:
            exist_ok:
        Returns:
        """
        self.mkdir(parents=True, exist_ok=exist_ok)

    @property
    def path(self):
        """
        返回字符串类型的路径
        Returns:
        """
        return str(self)

    @property
    def parent(self) -> 'MyPath':
        """
        返回父目录的新的MyPath对象
        :return:
        """
        drv = self.drive
        root = self.root
        parts = self.parts
        if len(parts) == 1 and (drv or root):
            return self
        return self.__class__(*parts[:-1])


class MyWindowsPath(MyPath, WindowsPath):
    pass


class MyPosixPath(MyPath, PosixPath):
    pass
